package com.app.common.download

import okhttp3.*
import java.io.*
import java.lang.Exception

open class HttpDownloader(private val saveDirectory: File) :DownloaderDelegate(), Callback {
    private var okHttpClient: OkHttpClient = OkHttpClient.Builder().apply {
        dispatcher(Dispatcher().apply { maxRequests = 1 })
    }.build()
    private val mapCall = mutableMapOf<String,Pair<Call,String>>()

    override fun internalAddWork(path: String,fileName:String) {

        val call = okHttpClient.newCall(Request.Builder().url(path).build())
        mapCall[path] = call to fileName
        call.enqueue(this)
    }

    override fun release() {
        super.release()
        mapCall.clear()
        okHttpClient.dispatcher().cancelAll()
    }

    override fun internalCancelWork(path: String) {
        mapCall.remove(path)?.first?.cancel()
    }

    override fun internalCancelAll() {
        mapCall.clear()
        okHttpClient.dispatcher().cancelAll()
    }

    override fun onFailure(call: Call, e: IOException) {
        val path = call.request().url().toString()
        workComplete(path,"",false,e)

        e.printStackTrace()
    }

    override fun onResponse(call: Call, response: Response) {
        val path = call.request().url().toString()
        var fileName = mapCall[path]?.second.orEmpty()
        if (fileName.isEmpty()) {
            fileName = File(path).name
        }
        if (response.isSuccessful) {
            val body = response.body()
            if (body!=null) {
                readStream(path,fileName,body.byteStream(),body.contentLength())
            }else{
                workComplete(path,"",false, Exception("body == null"))
            }
        }else{
            workComplete(path,"",false, Exception("response.isSuccessful == false"))
        }
    }

    private fun createTmpFile(name: String) = File(saveDirectory, "$name.tmp")
    private fun File.receivedSuccess(name: String): File {
        val f = File(saveDirectory,name)
        renameTo(f)
        return f
    }

    private fun readStream(path: String, fileName: String, ins: InputStream, length:Long){
        workBegin(path)
        val revBuff = ByteArray(1024*500)
        val tmpFile = createTmpFile(fileName)

        var out: OutputStream? = null
        var totalRevSize = 0L
        var error: Exception? = null
        try {
            var revSize = ins.read(revBuff)
            out = BufferedOutputStream(FileOutputStream(tmpFile))
            while (revSize>0){
                totalRevSize+=revSize
                out.write(revBuff,0,revSize)
                processing(path,totalRevSize,length)
                revSize = ins.read(revBuff)
            }
        }catch (e: Exception){
            error = e
        }finally {
            try {
                out?.close()
                out = null
            } catch (e: Exception) {
                //noop
            }
            val isCancel = mapCall[path] == null
            val localPath = if (error == null) {
                tmpFile.receivedSuccess(fileName).absolutePath
            }else{
                tmpFile.delete()
                ""
            }
            workComplete(path,localPath,isCancel,error)
        }

    }
}